import backtrader as bt

class StdDevStrategy(bt.Strategy):  
    params = (  
        ('period', 20),  # 计算标准差的周期  
        ('low_threshold', 30),  # 标准差低于此阈值时买入  
        ('high_threshold', 80),  # 标准差高于此阈值时卖出或保持  
    )  

    def __init__(self):  
        self.dataclose = self.datas[0].close
        #===标准差===
        self.std_dev = bt.indicators.StdDev(self.dataclose, period=self.params.period)
        
        self.print_list = [] #储存交易信息 
        
        self.ordersta=''

    def downcast(self, amount, lot): #可买入资金
	    #使用整数除法 amount // lot 取整
        return abs(amount // lot * lot) 
    
    def next(self):  
        if not self.position:  
            if self.std_dev[0] < self.params.low_threshold:  
                self.buy_price = self.data.close[0]
                #print('执行买入') 
                self.ordersta='执行买入'
                #self.print_list.append(self.ordersta)
                order_value = self.broker.getvalue() * 0.98
                order_amount = self.downcast(order_value / self.data.close[0], 100)
                self.order = self.buy(self.datas[0], order_amount, name=self.datas[0]._name)
                    
        else:  
            if self.std_dev[0] > self.params.high_threshold:  
                ## 执行卖出
                #print('执行卖出') 
                self.ordersta='执行卖出'
                #self.print_list.append(self.ordersta)
                self.order = self.order_target_percent(self.datas[0], 0, name=self.datas[0]._name)
                #self.log(f'卖{self.datas[0]._name},price:{self.data.close[0]:.2f},pct: 0')

    # ==================打印信息============================================
    def notify_order(self, order): # 用于通知和处理订单信息。
        # 检查order.status是否属于两个可能的状态之一：order.Submitted 和 order.Accepted。
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enough cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            if order.isbuy():
                self.log(
                            f"执行:{self.ordersta},"
                            f"代码:{order.info['name']},"
                            f"成交量:{order.executed.size},"
                            f"成交价:{order.executed.price:.2f},"
                            f"当前资产:{self.broker.getvalue():.2f}"
                        )
            elif order.issell():
                self.log(
                            f"执行:{self.ordersta},"
                            f"代码:{order.info['name']},"
                            f"成交量:{order.executed.size},"
                            f"成交价:{order.executed.price:.2f}," 
                            f"当前资产:{self.broker.getvalue():.2f}"
                        )
            self.bar_executed = len(self)

        # Write down: no pending order
        self.order = None

    def log(self, txt, dt=None): #日志-日期和信息
        """
        输出日期
        :param txt:
        :param dt:
        :return:
        """
        dt = dt or self.datetime.date(0)  # 现在的日期
        #'%s , %s' 是一个字符串模板，其中%s是一个占位符，用于表示一个字符串。
        # 在这个模板中，有两个%s，这意味着我们需要为它们提供两个字符串值。
        # (dt.isoformat(), txt) 是一个元组，它包含了两个值：dt.isoformat() 和 txt。
        # isoformat方法默认会生成一个包含T的ISO 8601格式的字符串（如2023-10-23T14:30:00），
        # 所以在输出的日期和时间之间会有一个T
        print('%s , %s' % (dt.isoformat(), txt))
        self.print_list.append('日期: %s , %s' % (dt.isoformat(), txt))
    
    def get_print_list(self):
        return self.print_list
            